use std::cmp::Ordering;
use std::time::{SystemTime, UNIX_EPOCH};
static mut NAMES: Vec<String> = Vec::new();
const AL: [&'static str; 26] = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];

pub struct Robot {
    codes: String,
}

impl Robot {
    pub fn new() -> Self {
        let mut result = String::new();
        Self::get_random_name(&mut result);
        Robot { codes: result }
    }

    pub fn get_random_name(result: &mut String) {
        let mut time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|v| v.as_nanos())
            .unwrap_or(0);

        let i1 = (time % 26) as usize;

        time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|v| v.as_nanos())
            .unwrap_or(0);

        let i2 = (time % 26) as usize;

        let c1 = AL[i1];
        let c2 = AL[i2];

        result.push_str(c1);
        result.push_str(c2);
        match i1.cmp(&10) {
            Ordering::Less => {
                result.push_str("0");
                result.push_str(i1.to_string().as_str());
            }
            Ordering::Equal => {
                result.push_str("10");
            }
            Ordering::Greater => {
                result.push_str(i1.to_string().as_str());
            }
        }
        let i = i2 % 10;
        result.push_str(i.to_string().as_str());
        unsafe {
            if NAMES.contains(result) {
                result.clear();
                Self::get_random_name(result);
            } else {
                NAMES.push(result.clone());
            }
        }
    }

    pub fn name(&self) -> &str {
        self.codes.as_str()
    }

    pub fn reset_name(&mut self) {
        unsafe {
            NAMES.retain(|v| v != &self.codes)
        }
        let mut result = String::new();
        Self::get_random_name(&mut result);
        self.codes = result
    }
}


fn assert_name_matches_pattern(n: &str) {
    assert_eq!(n.len(), 5, "name is exactly 5 characters long");
    assert!(
        n[0..2].chars().all(|c| c.is_ascii_uppercase()),
        "name starts with 2 uppercase letters"
    );
    assert!(
        n[2..].chars().all(|c| c.is_ascii_digit()),
        "name ends with 3 numbers"
    );
}

fn assert_name_is_persistent(r: &Robot) {
    // The type system already proves this, but why not.
    let n1 = r.name();
    let n2 = r.name();
    let n3 = r.name();
    assert_eq!(n1, n2);
    assert_eq!(n2, n3);
}

#[test]
#[ignore]
fn many_different_robots_have_different_names() {
    use std::collections::HashSet;
    // In 3,529 random robot names, there is ~99.99% chance of a name collision
    let vec: Vec<_> = (0..3529).map(|_| Robot::new()).collect();
    let set: HashSet<_> = vec.iter().map(|robot| robot.name()).collect();
    let number_of_collisions = vec.len() - set.len();
    assert_eq!(number_of_collisions, 0);
}